#include <string>
#include <vector>
#include <unordered_map>
#include <queue>
#include <pthread.h>
#include "Task.hpp"

using namespace std;

//在哪join线程?   -->   其实可以不用join
//线程池可看作是只有一个生产者的特殊的生产者-消费者模型 
//主线程调push任务时只加锁不判断，会导致溢出吗?
//如何获取线程名字?

static const int DefaultThreadNum = 5;

class ThreadData
{
public:
    ThreadData(int no = 0,const string& name = "thread-")
    :_name(name + to_string(no)), _tid(0)
    {       
    }
    string _name;
    pthread_t _tid;
};

template<class T>
class ThreadPool
{

//封装加锁解锁和休眠唤醒
private:
    void Lock()
    {
        pthread_mutex_lock(&_lock);
    }

    void UnLock()
    {
        pthread_mutex_unlock(&_lock);
    }

    void WakeUp()
    {
        pthread_cond_signal(&_cond);
    }

    void ThreadSleep()
    {
        pthread_cond_wait(&_cond,&_lock);
    }

    bool IsQueueEmpty()
    {
        return _tasks.empty();
    }

    string GetThreadName(pthread_t tid)
    {
        return _hashOfTd[tid]._name;
    }

public:
    static void* HandleTask(void* arg)      //静态成员函数无法直接访问类内成员，所以传this指针！
    {
        ThreadPool<T>* tp = static_cast<ThreadPool<T>*>(arg);
        while(1)
        {
            //1.获取任务
            tp->Lock();
            while(tp->IsQueueEmpty())   //防误唤醒！在这加锁就行，不要再在Pop中加锁了-->否则就卡住了(因为此时状态是持有锁而又申请锁！)
            {
                tp->ThreadSleep();
            }
            Task t = tp->Pop();
            tp->UnLock();

            //2.加工处理任务
            t();
            cout << tp->GetThreadName(pthread_self()) << " handle a task: " << t.GetTaskLog() << ",result is: " << t.GetResultLog() << endl;            
        }

        return nullptr;
    }

    //将创建一批线程的工作封装
    void Start()
    {  
        for(int i = 0; i < DefaultThreadNum; i++)
        {
            ThreadData td = ThreadData(i);
            pthread_create(&td._tid,nullptr,HandleTask,this);
            _hashOfTd[td._tid] = td;
        }
    }

    void Push(const T& in)
    {
        Lock();
        _tasks.push(in);
        WakeUp();
        UnLock();
    }

    T Pop()     
    {
        T t = _tasks.front();
        _tasks.pop();
        return t;
    }

    ThreadPool(int threadnum = DefaultThreadNum)
    {
        //_tds = vector<ThreadData>(threadnum);
        pthread_mutex_init(&_lock,nullptr);
        pthread_cond_init(&_cond,nullptr);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_cond);
    }

private:
    //vector<ThreadData> _tds;
    unordered_map<pthread_t,ThreadData> _hashOfTd;      //便于获取线程信息
    queue<T> _tasks;
    pthread_mutex_t _lock;
    pthread_cond_t _cond;
};